/*-------------------------------------------------------------------------
 *
 * tsquery.c
 *	  tsquery的输入/输出函数
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/tsquery.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_type.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/pg_crc.h"

/* FTS 操作符优先级，见 ts_type.h */
const int	tsearch_op_priority[OP_COUNT] =
{
	4,							/* OP_NOT */
	2,							/* OP_AND */
	1,							/* OP_OR */
	3							/* OP_PHRASE */
};

/*
 * 解析器的状态
 */
typedef enum
{
	WAITOPERAND = 1,
	WAITOPERATOR = 2,
	WAITFIRSTOPERAND = 3
} ts_parserstate;

/*
 * 用于解析的标记类型
 */
typedef enum
{
	PT_END = 0,
	PT_ERR = 1,
	PT_VAL = 2,
	PT_OPR = 3,
	PT_OPEN = 4,
	PT_CLOSE = 5
} ts_tokentype;

/*
 * 从查询字符串中获取标记
 *
 * *operator 在返回值为 PT_OPR 时填充为 OP_*，
 * 但 *weight 可能在短语操作符的情况下包含距离值。
 * 当返回值为 PT_VAL 时，*strval, *lenval 和 *weight 被填充
 *
 */
typedef ts_tokentype (*ts_tokenizer) (TSQueryParserState state, int8 *operator,
									  int *lenval, char **strval,
									  int16 *weight, bool *prefix);

struct TSQueryParserStateData
{
	/* 用于解析 tsquery 的标记器 */
	ts_tokenizer gettoken;

	/* 标记器函数的状态 */
	char	   *buffer;			/* 我们正在扫描的整个字符串 */
	char	   *buf;			/* 当前扫描点 */
	int			count;			/* 嵌套计数，由 ( 增加，
								 * 由 ) 减少 */
	ts_parserstate state;

	/* 在列表中的波兰（前缀）表示法，由 push* 函数填充 */
	List	   *polstr;

	/*
	 * 操作数的字符串在 op 中收集。curop 是指向
	 * op 的已使用空间末尾的指针。
	 */
	char	   *op;
	char	   *curop;
	int			lenop;			/* op 的分配大小 */
	int			sumlen;			/* op 的使用大小 */

	/* 值解析器的状态 */
	TSVectorParseState valstate;
};

/*
 * 用于解析修饰符（重量和前缀标志当前）部分的子程序，
 * 如查询的 ':AB*'
 */
static char * get_modifiers(char *fc_buf, int16 *fc_weight, bool *fc_prefix)
{
	*fc_weight = 0;
	*fc_prefix = false;

	if (!t_iseq(fc_buf, ':'))
		return fc_buf;

	fc_buf++;
	while (*fc_buf && pg_mblen(fc_buf) == 1)
	{
		switch (*fc_buf)
		{
			case 'a':
			case 'A':
				*fc_weight |= 1 << 3;
				break;
			case 'b':
			case 'B':
				*fc_weight |= 1 << 2;
				break;
			case 'c':
			case 'C':
				*fc_weight |= 1 << 1;
				break;
			case 'd':
			case 'D':
				*fc_weight |= 1;
				break;
			case '*':
				*fc_prefix = true;
				break;
			default:
				return fc_buf;
		}
		fc_buf++;
	}

	return fc_buf;
}

/*
 * 解析短语操作符。该操作符
 * 可以采用以下形式：
 *
 *		a <N> b（距离恰好为 N 个词素）
 *		a <-> b（默认距离 = 1）
 *
 * 缓冲区应以 '<' 字符开头
 */
static bool fc_parse_phrase_operator(TSQueryParserState fc_pstate, int16 *fc_distance)
{
	enum
	{
		PHRASE_OPEN = 0,
		PHRASE_DIST,
		PHRASE_CLOSE,
		PHRASE_FINISH
	}			fc_state = PHRASE_OPEN;
	char	   *fc_ptr = fc_pstate->buf;
	char	   *fc_endptr;
	long		fc_l = 1;			/* 默认距离 */

	while (*fc_ptr)
	{
		switch (fc_state)
		{
			case PHRASE_OPEN:
				if (t_iseq(fc_ptr, '<'))
				{
					fc_state = PHRASE_DIST;
					fc_ptr++;
				}
				else
					return false;
				break;

			case PHRASE_DIST:
				if (t_iseq(fc_ptr, '-'))
				{
					fc_state = PHRASE_CLOSE;
					fc_ptr++;
					continue;
				}

				if (!t_isdigit(fc_ptr))
					return false;

				errno = 0;
				fc_l = strtol(fc_ptr, &fc_endptr, 10);
				if (fc_ptr == fc_endptr)
					return false;
				else if (errno == ERANGE || fc_l < 0 || fc_l > MAXENTRYPOS)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("distance in phrase operator must be an integer value between zero and %d inclusive",
									MAXENTRYPOS)));
				else
				{
					fc_state = PHRASE_CLOSE;
					fc_ptr = fc_endptr;
				}
				break;

			case PHRASE_CLOSE:
				if (t_iseq(fc_ptr, '>'))
				{
					fc_state = PHRASE_FINISH;
					fc_ptr++;
				}
				else
					return false;
				break;

			case PHRASE_FINISH:
				*fc_distance = (int16) fc_l;
				fc_pstate->buf = fc_ptr;
				return true;
		}
	}

	return false;
}

/*
 * 解析在 websearch_to_tsquery() 中使用的 OR 操作符，如果我们
 * 认为 "OR" 字面量可以是一个操作符，则返回真
 */
static bool fc_parse_or_operator(TSQueryParserState fc_pstate)
{
	char	   *fc_ptr = fc_pstate->buf;

	/* 应该以 "OR" 字面量开始 */
	if (pg_strncasecmp(fc_ptr, "or", 2) != 0)
		return false;

	fc_ptr += 2;

	/*
	 * 它不应该是任何单词的一部分，但稍后应该是
	 * 某个操作数
	 */
	if (*fc_ptr == '\0')			/* 没有操作数 */
		return false;

	/* 它不应该是任何单词的一部分 */
	if (t_iseq(fc_ptr, '-') || t_iseq(fc_ptr, '_') || t_isalpha(fc_ptr) || t_isdigit(fc_ptr))
		return false;

	for (;;)
	{
		fc_ptr += pg_mblen(fc_ptr);

		if (*fc_ptr == '\0')		/* 在没有操作数的情况下到达字符串末尾 */
			return false;

		/*
		 * 假设我们找到了一个操作数，但可能不是一个正确的操作数。
		 * 所以我们仍然把 OR 字面量视为一个操作，可能有错误的
		 * 操作数，并且不会将其视为词素进行搜索
		 */
		if (!t_isspace(fc_ptr))
			break;
	}

	fc_pstate->buf += 2;
	return true;
}

static ts_tokentype fc_gettoken_query_standard(TSQueryParserState fc_state, int8 *fc_operator,
						int *fc_lenval, char **fc_strval,
						int16 *fc_weight, bool *fc_prefix)
{
	*fc_weight = 0;
	*fc_prefix = false;

	while (true)
	{
		switch (fc_state->state)
		{
			case WAITFIRSTOPERAND:
			case WAITOPERAND:
				if (t_iseq(fc_state->buf, '!'))
				{
					fc_state->buf++;
					fc_state->state = WAITOPERAND;
					*fc_operator = OP_NOT;
					return PT_OPR;
				}
				else if (t_iseq(fc_state->buf, '('))
				{
					fc_state->buf++;
					fc_state->state = WAITOPERAND;
					fc_state->count++;
					return PT_OPEN;
				}
				else if (t_iseq(fc_state->buf, ':'))
				{
					ereport(ERROR,
							(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("syntax error in tsquery: \"%s\"",
									fc_state->buffer)));
				}
				else if (!t_isspace(fc_state->buf))
				{
					/*
					 * 我们依赖 tsvector 解析器为我们解析值
					 */
					reset_tsvector_parser(fc_state->valstate, fc_state->buf);
					if (gettoken_tsvector(fc_state->valstate, fc_strval, fc_lenval,
										  NULL, NULL, &fc_state->buf))
					{
						fc_state->buf = get_modifiers(fc_state->buf, fc_weight, fc_prefix);
						fc_state->state = WAITOPERATOR;
						return PT_VAL;
					}
					else if (fc_state->state == WAITFIRSTOPERAND)
					{
						return PT_END;
					}
					else
						ereport(ERROR,
								(errcode(ERRCODE_SYNTAX_ERROR),
								 errmsg("no operand in tsquery: \"%s\"",
										fc_state->buffer)));
				}
				break;

			case WAITOPERATOR:
				if (t_iseq(fc_state->buf, '&'))
				{
					fc_state->buf++;
					fc_state->state = WAITOPERAND;
					*fc_operator = OP_AND;
					return PT_OPR;
				}
				else if (t_iseq(fc_state->buf, '|'))
				{
					fc_state->buf++;
					fc_state->state = WAITOPERAND;
					*fc_operator = OP_OR;
					return PT_OPR;
				}
				else if (fc_parse_phrase_operator(fc_state, fc_weight))
				{
					/* weight 变量用作距离的存储 */
					fc_state->state = WAITOPERAND;
					*fc_operator = OP_PHRASE;
					return PT_OPR;
				}
				else if (t_iseq(fc_state->buf, ')'))
				{
					fc_state->buf++;
					fc_state->count--;
					return (fc_state->count < 0) ? PT_ERR : PT_CLOSE;
				}
				else if (*fc_state->buf == '\0')
				{
					return (fc_state->count) ? PT_ERR : PT_END;
				}
				else if (!t_isspace(fc_state->buf))
				{
					return PT_ERR;
				}
				break;
		}

		fc_state->buf += pg_mblen(fc_state->buf);
	}
}

static ts_tokentype fc_gettoken_query_websearch(TSQueryParserState fc_state, int8 *fc_operator,
						 int *fc_lenval, char **fc_strval,
						 int16 *fc_weight, bool *fc_prefix)
{
	*fc_weight = 0;
	*fc_prefix = false;

	while (true)
	{
		switch (fc_state->state)
		{
			case WAITFIRSTOPERAND:
			case WAITOPERAND:
				if (t_iseq(fc_state->buf, '-'))
				{
					fc_state->buf++;
					fc_state->state = WAITOPERAND;

					*fc_operator = OP_NOT;
					return PT_OPR;
				}
				else if (t_iseq(fc_state->buf, '"'))
				{
					/* 引号中的所有内容作为单个标记处理 */

					/* 跳过开引号 */
					fc_state->buf++;
					*fc_strval = fc_state->buf;

					/* 迭代到闭引号或字符串末尾 */
					while (*fc_state->buf != '\0' && !t_iseq(fc_state->buf, '"'))
						fc_state->buf++;
					*fc_lenval = fc_state->buf - *fc_strval;

					/* 如果不是字符串末尾，则跳过闭引号 */
					if (*fc_state->buf != '\0')
						fc_state->buf++;

					fc_state->state = WAITOPERATOR;
					fc_state->count++;
					return PT_VAL;
				}
				else if (ISOPERATOR(fc_state->buf))
				{
					/* 忽略，否则 gettoken_tsvector() 将引发错误 */
					fc_state->buf++;
					fc_state->state = WAITOPERAND;
					continue;
				}
				else if (!t_isspace(fc_state->buf))
				{
					/*
					 * 我们依赖 tsvector 解析器为我们解析值
					 */
					reset_tsvector_parser(fc_state->valstate, fc_state->buf);
					if (gettoken_tsvector(fc_state->valstate, fc_strval, fc_lenval,
										  NULL, NULL, &fc_state->buf))
					{
						fc_state->state = WAITOPERATOR;
						return PT_VAL;
					}
					else if (fc_state->state == WAITFIRSTOPERAND)
					{
						return PT_END;
					}
					else
					{
						/* 最后，我们必须提供一个操作数 */
						pushStop(fc_state);
						return PT_END;
					}
				}
				break;

			case WAITOPERATOR:
				if (*fc_state->buf == '\0')
				{
					return PT_END;
				}
				else if (fc_parse_or_operator(fc_state))
				{
					fc_state->state = WAITOPERAND;
					*fc_operator = OP_OR;
					return PT_OPR;
				}
				else if (ISOPERATOR(fc_state->buf))
				{
					/* 在此状态下也忽略其他操作符 */
					fc_state->buf++;
					continue;
				}
				else if (!t_isspace(fc_state->buf))
				{
					/* 在操作数之间插入隐式 AND */
					fc_state->state = WAITOPERAND;
					*fc_operator = OP_AND;
					return PT_OPR;
				}
				break;
		}

		fc_state->buf += pg_mblen(fc_state->buf);
	}
}

static ts_tokentype fc_gettoken_query_plain(TSQueryParserState fc_state, int8 *fc_operator,
					 int *fc_lenval, char **fc_strval,
					 int16 *fc_weight, bool *fc_prefix)
{
	*fc_weight = 0;
	*fc_prefix = false;

	if (*fc_state->buf == '\0')
		return PT_END;

	*fc_strval = fc_state->buf;
	*fc_lenval = strlen(fc_state->buf);
	fc_state->buf += *fc_lenval;
	fc_state->count++;
	return PT_VAL;
}

/*
 * 将操作符推送到 state->polstr
 */
void pushOperator(TSQueryParserState fc_state, int8 fc_oper, int16 fc_distance)
{
	QueryOperator *fc_tmp;

	Assert(fc_oper == OP_NOT || fc_oper == OP_AND || fc_oper == OP_OR || fc_oper == OP_PHRASE);

	fc_tmp = (QueryOperator *) palloc0(sizeof(QueryOperator));
	fc_tmp->type = QI_OPR;
	fc_tmp->oper = fc_oper;
	fc_tmp->distance = (fc_oper == OP_PHRASE) ? fc_distance : 0;
	/* 左侧稍后使用 findoprnd 填充 */

	fc_state->polstr = lcons(fc_tmp, fc_state->polstr);
}

static void fc_pushValue_internal(TSQueryParserState fc_state, pg_crc32 fc_valcrc, int fc_distance, int fc_lenval, int fc_weight, bool fc_prefix)
{
	QueryOperand *fc_tmp;

	if (fc_distance >= MAXSTRPOS)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("value is too big in tsquery: \"%s\"",
						fc_state->buffer)));
	if (fc_lenval >= MAXSTRLEN)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("operand is too long in tsquery: \"%s\"",
						fc_state->buffer)));

	fc_tmp = (QueryOperand *) palloc0(sizeof(QueryOperand));
	fc_tmp->type = QI_VAL;
	fc_tmp->weight = fc_weight;
	fc_tmp->prefix = fc_prefix;
	fc_tmp->valcrc = (int32) fc_valcrc;
	fc_tmp->length = fc_lenval;
	fc_tmp->distance = fc_distance;

	fc_state->polstr = lcons(fc_tmp, fc_state->polstr);
}

/*
 * 将操作数推送到 state->polstr。
 *
 * strval 必须指向一个字符串，该字符串等于 state->curop。lenval 是字符串的长度。
 */
void pushValue(TSQueryParserState fc_state, char *fc_strval, int fc_lenval, int16 fc_weight, bool fc_prefix)
{
	pg_crc32	fc_valcrc;

	if (fc_lenval >= MAXSTRLEN)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("word is too long in tsquery: \"%s\"",
						fc_state->buffer)));

	INIT_LEGACY_CRC32(fc_valcrc);
	COMP_LEGACY_CRC32(fc_valcrc, fc_strval, fc_lenval);
	FIN_LEGACY_CRC32(fc_valcrc);
	fc_pushValue_internal(fc_state, fc_valcrc, fc_state->curop - fc_state->op, fc_lenval, fc_weight, fc_prefix);

	/* 将值字符串附加到 state.op，如果需要则先扩大缓冲区 */
	while (fc_state->curop - fc_state->op + fc_lenval + 1 >= fc_state->lenop)
	{
		int			fc_used = fc_state->curop - fc_state->op;

		fc_state->lenop *= 2;
		fc_state->op = (char *) repalloc((void *) fc_state->op, fc_state->lenop);
		fc_state->curop = fc_state->op + fc_used;
	}
	memcpy((void *) fc_state->curop, (void *) fc_strval, fc_lenval);
	fc_state->curop += fc_lenval;
	*(fc_state->curop) = '\0';
	fc_state->curop++;
	fc_state->sumlen += fc_lenval + 1 /* \0 */ ;
}


/*
 * 将停用词占位符推送到 state->polstr
 */
void pushStop(TSQueryParserState fc_state)
{
	QueryOperand *fc_tmp;

	fc_tmp = (QueryOperand *) palloc0(sizeof(QueryOperand));
	fc_tmp->type = QI_VALSTOP;

	fc_state->polstr = lcons(fc_tmp, fc_state->polstr);
}


#define STACKDEPTH	32

typedef struct OperatorElement
{
	int8		op;
	int16		distance;
} OperatorElement;

static void fc_pushOpStack(OperatorElement *fc_stack, int *fc_lenstack, int8 fc_op, int16 fc_distance)
{
	if (*fc_lenstack == STACKDEPTH)	/* 内部错误 */
		elog(ERROR, "tsquery stack too small");

	fc_stack[*fc_lenstack].op = fc_op;
	fc_stack[*fc_lenstack].distance = fc_distance;

	(*fc_lenstack)++;
}

static void fc_cleanOpStack(TSQueryParserState fc_state,
			 OperatorElement *fc_stack, int *fc_lenstack, int8 fc_op)
{
	int			fc_opPriority = OP_PRIORITY(fc_op);

	while (*fc_lenstack)
	{
		/* NOT 是右结合的，与其他不同 */
		if ((fc_op != OP_NOT && fc_opPriority > OP_PRIORITY(fc_stack[*fc_lenstack - 1].op)) ||
			(fc_op == OP_NOT && fc_opPriority >= OP_PRIORITY(fc_stack[*fc_lenstack - 1].op)))
			break;

		(*fc_lenstack)--;
		pushOperator(fc_state, fc_stack[*fc_lenstack].op,
					 fc_stack[*fc_lenstack].distance);
	}
}

/*
 * 生成查询的波兰（前缀）表示法。
 *
 * 有关 pushval 的解释，请参见 parse_tsquery。
 */
static void fc_makepol(TSQueryParserState fc_state,
		PushFunction fc_pushval,
		Datum fc_opaque)
{
	int8		fc_operator = 0;
	ts_tokentype fc_type;
	int			fc_lenval = 0;
	char	   *fc_strval = NULL;
	OperatorElement fc_opstack[STACKDEPTH];
	int			fc_lenstack = 0;
	int16		fc_weight = 0;
	bool		fc_prefix;

	/* 由于这个函数递归，可能会导致栈溢出 */
	check_stack_depth();

	while ((fc_type = fc_state->gettoken(fc_state, &fc_operator,
								   &fc_lenval, &fc_strval,
								   &fc_weight, &fc_prefix)) != PT_END)
	{
		switch (fc_type)
		{
			case PT_VAL:
				fc_pushval(fc_opaque, fc_state, fc_strval, fc_lenval, fc_weight, fc_prefix);
				break;
			case PT_OPR:
				fc_cleanOpStack(fc_state, fc_opstack, &fc_lenstack, fc_operator);
				fc_pushOpStack(fc_opstack, &fc_lenstack, fc_operator, fc_weight);
				break;
			case PT_OPEN:
				fc_makepol(fc_state, fc_pushval, fc_opaque);
				break;
			case PT_CLOSE:
				fc_cleanOpStack(fc_state, fc_opstack, &fc_lenstack, OP_OR /* lowest */ );
				return;
			case PT_ERR:
			default:
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("syntax error in tsquery: \"%s\"",
								fc_state->buffer)));
		}
	}

	fc_cleanOpStack(fc_state, fc_opstack, &fc_lenstack, OP_OR /* lowest */ );
}

static void fc_findoprnd_recurse(QueryItem *fc_ptr, uint32 *fc_pos, int fc_nnodes, bool *fc_needcleanup)
{
	/* 由于这个函数递归，它可能导致栈溢出。 */
	check_stack_depth();

	if (*fc_pos >= fc_nnodes)
		elog(ERROR, "malformed tsquery: operand not found");

	if (fc_ptr[*fc_pos].type == QI_VAL)
	{
		(*fc_pos)++;
	}
	else if (fc_ptr[*fc_pos].type == QI_VALSTOP)
	{
		*fc_needcleanup = true;	/* 我们必须删除停用词 */
		(*fc_pos)++;
	}
	else
	{
		Assert(fc_ptr[*fc_pos].type == QI_OPR);

		if (fc_ptr[*fc_pos].qoperator.oper == OP_NOT)
		{
			fc_ptr[*fc_pos].qoperator.left = 1;	/* 固定偏移量 */
			(*fc_pos)++;

			/* 处理唯一的参数 */
			fc_findoprnd_recurse(fc_ptr, fc_pos, fc_nnodes, fc_needcleanup);
		}
		else
		{
			QueryOperator *fc_curitem = &fc_ptr[*fc_pos].qoperator;
			int			fc_tmp = *fc_pos; /* 保存当前位置信息 */

			Assert(fc_curitem->oper == OP_AND ||
				   fc_curitem->oper == OP_OR ||
				   fc_curitem->oper == OP_PHRASE);

			(*fc_pos)++;

			/* 处理右侧参数 */
			fc_findoprnd_recurse(fc_ptr, fc_pos, fc_nnodes, fc_needcleanup);

			fc_curitem->left = *fc_pos - fc_tmp; /* 设置左侧参数的偏移量 */

			/* 处理左侧参数 */
			fc_findoprnd_recurse(fc_ptr, fc_pos, fc_nnodes, fc_needcleanup);
		}
	}
}


/*
 * 填写之前未填写的左侧字段。
 * 输入的 QueryItems 必须采用波兰前缀（prefix）表示法。
 * 如果有任何 QI_VALSTOP 节点，还要将 *needcleanup 设置为 true。
 */
static void fc_findoprnd(QueryItem *fc_ptr, int fc_size, bool *fc_needcleanup)
{
	uint32		fc_pos;

	*fc_needcleanup = false;
	fc_pos = 0;
	fc_findoprnd_recurse(fc_ptr, &fc_pos, fc_size, fc_needcleanup);

	if (fc_pos != fc_size)
		elog(ERROR, "malformed tsquery: extra nodes");
}


/*
 * 查询中的每个值（操作数）都传递给 pushval。pushval 可以
 * 使用 pushValue 和 pushOperator 将简单值转换为任意复杂的表达式。它必须使用 pushValue 推送单个值，
 * 使用所有操作数的完整表达式进行推送，或使用 pushStop 推送停用词占位符，
 * 否则前缀表示法表示形式将被破坏，导致操作符没有操作数。
 *
 * opaque 原样传递给 pushval，pushval 可以用它存储其
 * 私有状态。
 */
TSQuery parse_tsquery(char *fc_buf,
			  PushFunction fc_pushval,
			  Datum fc_opaque,
			  int fc_flags)
{
	struct TSQueryParserStateData fc_state;
	int			fc_i;
	TSQuery		fc_query;
	int			fc_commonlen;
	QueryItem  *fc_ptr;
	ListCell   *fc_cell;
	bool		fc_needcleanup;
	int			fc_tsv_flags = P_TSV_OPR_IS_DELIM | P_TSV_IS_TSQUERY;

	/* plain 不应与 web 一起使用 */
	Assert((fc_flags & (P_TSQ_PLAIN | P_TSQ_WEB)) != (P_TSQ_PLAIN | P_TSQ_WEB));

	/* 选择合适的分词器 */
	if (fc_flags & P_TSQ_PLAIN)
		fc_state.gettoken = fc_gettoken_query_plain;
	else if (fc_flags & P_TSQ_WEB)
	{
		fc_state.gettoken = fc_gettoken_query_websearch;
		fc_tsv_flags |= P_TSV_IS_WEB;
	}
	else
		fc_state.gettoken = fc_gettoken_query_standard;

	/* 初始化状态 */
	fc_state.buffer = fc_buf;
	fc_state.buf = fc_buf;
	fc_state.count = 0;
	fc_state.state = WAITFIRSTOPERAND;
	fc_state.polstr = NIL;

	/* 初始化值解析器的状态 */
	fc_state.valstate = init_tsvector_parser(fc_state.buffer, fc_tsv_flags);

	/* 初始化操作数列表 */
	fc_state.sumlen = 0;
	fc_state.lenop = 64;
	fc_state.curop = fc_state.op = (char *) palloc(fc_state.lenop);
	*(fc_state.curop) = '\0';

	/* 解析查询并生成波兰表示法（后缀，但顺序反向） */
	fc_makepol(&fc_state, fc_pushval, fc_opaque);

	close_tsvector_parser(fc_state.valstate);

	if (list_length(fc_state.polstr) == 0)
	{
		ereport(NOTICE,
				(errmsg("text-search query doesn't contain lexemes: \"%s\"",
						fc_state.buffer)));
		fc_query = (TSQuery) palloc(HDRSIZETQ);
		SET_VARSIZE(fc_query, HDRSIZETQ);
		fc_query->size = 0;
		return fc_query;
	}

	if (TSQUERY_TOO_BIG(list_length(fc_state.polstr), fc_state.sumlen))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("tsquery is too large")));
	fc_commonlen = COMPUTESIZE(list_length(fc_state.polstr), fc_state.sumlen);

	/* 将 QueryItems 打包到最终的 TSQuery 结构中以返回给调用者 */
	fc_query = (TSQuery) palloc0(fc_commonlen);
	SET_VARSIZE(fc_query, fc_commonlen);
	fc_query->size = list_length(fc_state.polstr);
	fc_ptr = GETQUERY(fc_query);

	/* 将 QueryItems 复制到 TSQuery */
	fc_i = 0;
	foreach(fc_cell, fc_state.polstr)
	{
		QueryItem  *fc_item = (QueryItem *) lfirst(fc_cell);

		switch (fc_item->type)
		{
			case QI_VAL:
				memcpy(&fc_ptr[fc_i], fc_item, sizeof(QueryOperand));
				break;
			case QI_VALSTOP:
				fc_ptr[fc_i].type = QI_VALSTOP;
				break;
			case QI_OPR:
				memcpy(&fc_ptr[fc_i], fc_item, sizeof(QueryOperator));
				break;
			default:
				elog(ERROR, "unrecognized QueryItem type: %d", fc_item->type);
		}
		fc_i++;
	}

	/* 将所有操作数字符串复制到 TSQuery */
	memcpy((void *) GETOPERAND(fc_query), (void *) fc_state.op, fc_state.sumlen);
	pfree(fc_state.op);

	/*
	 * 为每个操作符设置左侧操作数指针。顺便检查一下，
	 * 是否存在任何 QI_VALSTOP 节点。
	 */
	fc_findoprnd(fc_ptr, fc_query->size, &fc_needcleanup);

	/*
	 * 如果存在 QI_VALSTOP 节点，删除它们并简化树。
	 */
	if (fc_needcleanup)
		fc_query = cleanup_tsquery_stopwords(fc_query);

	return fc_query;
}

static void fc_pushval_asis(Datum fc_opaque, TSQueryParserState fc_state, char *fc_strval, int fc_lenval,
			 int16 fc_weight, bool fc_prefix)
{
	pushValue(fc_state, fc_strval, fc_lenval, fc_weight, fc_prefix);
}

/*
 * 不带形态学的
 */
Datum tsqueryin(PG_FUNCTION_ARGS)
{
	char	   *fc_in = PG_GETARG_CSTRING(0);

	PG_RETURN_TSQUERY(parse_tsquery(fc_in, fc_pushval_asis, PointerGetDatum(NULL), 0));
}

/*
 * 输出函数
 */
typedef struct
{
	QueryItem  *curpol;
	char	   *buf;
	char	   *cur;
	char	   *op;
	int			buflen;
} INFIX;

/* 确保 inf->buf 足够大以添加 'addsize' 字节 */
#define RESIZEBUF(inf, addsize) \
while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
{ \
	int fc_len = (inf)->cur - (inf)->buf; \
	(inf)->buflen *= 2; \
	(inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
	(inf)->cur = (inf)->buf + fc_len; \
}

/*
 * 递归遍历树并
 * 以中缀（人可读）形式打印
 */
static void fc_infix(INFIX *fc_in, int fc_parentPriority, bool fc_rightPhraseOp)
{
	/* 由于这个函数递归，它可能导致栈溢出。 */
	check_stack_depth();

	if (fc_in->curpol->type == QI_VAL)
	{
		QueryOperand *fc_curpol = &fc_in->curpol->qoperand;
		char	   *fc_op = fc_in->op + fc_curpol->distance;
		int			fc_clen;

		RESIZEBUF(fc_in, fc_curpol->length * (pg_database_encoding_max_length() + 1) + 2 + 6);
		*(fc_in->cur) = '\'';
		fc_in->cur++;
		while (*fc_op)
		{
			if (t_iseq(fc_op, '\''))
			{
				*(fc_in->cur) = '\'';
				fc_in->cur++;
			}
			else if (t_iseq(fc_op, '\\'))
			{
				*(fc_in->cur) = '\\';
				fc_in->cur++;
			}
			COPYCHAR(fc_in->cur, fc_op);

			fc_clen = pg_mblen(fc_op);
			fc_op += fc_clen;
			fc_in->cur += fc_clen;
		}
		*(fc_in->cur) = '\'';
		fc_in->cur++;
		if (fc_curpol->weight || fc_curpol->prefix)
		{
			*(fc_in->cur) = ':';
			fc_in->cur++;
			if (fc_curpol->prefix)
			{
				*(fc_in->cur) = '*';
				fc_in->cur++;
			}
			if (fc_curpol->weight & (1 << 3))
			{
				*(fc_in->cur) = 'A';
				fc_in->cur++;
			}
			if (fc_curpol->weight & (1 << 2))
			{
				*(fc_in->cur) = 'B';
				fc_in->cur++;
			}
			if (fc_curpol->weight & (1 << 1))
			{
				*(fc_in->cur) = 'C';
				fc_in->cur++;
			}
			if (fc_curpol->weight & 1)
			{
				*(fc_in->cur) = 'D';
				fc_in->cur++;
			}
		}
		*(fc_in->cur) = '\0';
		fc_in->curpol++;
	}
	else if (fc_in->curpol->qoperator.oper == OP_NOT)
	{
		int			fc_priority = QO_PRIORITY(fc_in->curpol);

		if (fc_priority < fc_parentPriority)
		{
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, "( ");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}
		RESIZEBUF(fc_in, 1);
		*(fc_in->cur) = '!';
		fc_in->cur++;
		*(fc_in->cur) = '\0';
		fc_in->curpol++;

		fc_infix(fc_in, fc_priority, false);
		if (fc_priority < fc_parentPriority)
		{
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, " )");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}
	}
	else
	{
		int8		fc_op = fc_in->curpol->qoperator.oper;
		int			fc_priority = QO_PRIORITY(fc_in->curpol);
		int16		fc_distance = fc_in->curpol->qoperator.distance;
		INFIX		fc_nrm;
		bool		fc_needParenthesis = false;

		fc_in->curpol++;
		if (fc_priority < fc_parentPriority ||
		/* 短语操作符依赖于顺序 */
			(fc_op == OP_PHRASE && fc_rightPhraseOp))
		{
			fc_needParenthesis = true;
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, "( ");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}

		fc_nrm.curpol = fc_in->curpol;
		fc_nrm.op = fc_in->op;
		fc_nrm.buflen = 16;
		fc_nrm.cur = fc_nrm.buf = (char *) palloc(sizeof(char) * fc_nrm.buflen);

		/* 获取右操作数 */
		fc_infix(&fc_nrm, fc_priority, (fc_op == OP_PHRASE));

		/* 获取并打印左操作数 */
		fc_in->curpol = fc_nrm.curpol;
		fc_infix(fc_in, fc_priority, false);

		/* 打印操作符和右操作数 */
		RESIZEBUF(fc_in, 3 + (2 + 10 /* distance */ ) + (fc_nrm.cur - fc_nrm.buf));
		switch (fc_op)
		{
			case OP_OR:
				sprintf(fc_in->cur, " | %s", fc_nrm.buf);
				break;
			case OP_AND:
				sprintf(fc_in->cur, " & %s", fc_nrm.buf);
				break;
			case OP_PHRASE:
				if (fc_distance != 1)
					sprintf(fc_in->cur, " <%d> %s", fc_distance, fc_nrm.buf);
				else
					sprintf(fc_in->cur, " <-> %s", fc_nrm.buf);
				break;
			default:
				/* OP_NOT 在上面的 if 分支中处理 */
				elog(ERROR, "unrecognized operator type: %d", fc_op);
		}
		fc_in->cur = strchr(fc_in->cur, '\0');
		pfree(fc_nrm.buf);

		if (fc_needParenthesis)
		{
			RESIZEBUF(fc_in, 2);
			sprintf(fc_in->cur, " )");
			fc_in->cur = strchr(fc_in->cur, '\0');
		}
	}
}

Datum tsqueryout(PG_FUNCTION_ARGS)
{
	TSQuery		fc_query = PG_GETARG_TSQUERY(0);
	INFIX		fc_nrm;

	if (fc_query->size == 0)
	{
		char	   *fc_b = palloc(1);

		*fc_b = '\0';
		PG_RETURN_POINTER(fc_b);
	}
	fc_nrm.curpol = GETQUERY(fc_query);
	fc_nrm.buflen = 32;
	fc_nrm.cur = fc_nrm.buf = (char *) palloc(sizeof(char) * fc_nrm.buflen);
	*(fc_nrm.cur) = '\0';
	fc_nrm.op = GETOPERAND(fc_query);
	fc_infix(&fc_nrm, -1 /* 优先级最低 */ , false);

	PG_FREE_IF_COPY(fc_query, 0);
	PG_RETURN_CSTRING(fc_nrm.buf);
}

/*
 * 二进制输入 / 输出函数。二进制格式如下：
 *
 * uint32	 查询中的操作符 / 操作数数量
 *
 * 后面跟着前缀表示法中的操作符和操作数。对于每个
 * 操作数：
 *
 * uint8	类型，QI_VAL
 * uint8	权重
 *			客户端编码中的操作数字符串，以 null 结束
 * uint8	前缀
 *
 * 对于每个操作符：
 * uint8	类型，QI_OPR
 * uint8	操作符，之一为 OP_AND、OP_PHRASE、OP_OR、OP_NOT。
 * uint16	距离（仅适用于 OP_PHRASE）
 */
Datum tsquerysend(PG_FUNCTION_ARGS)
{
	TSQuery		fc_query = PG_GETARG_TSQUERY(0);
	StringInfoData fc_buf;
	int			fc_i;
	QueryItem  *fc_item = GETQUERY(fc_query);

	pq_begintypsend(&fc_buf);

	pq_sendint32(&fc_buf, fc_query->size);
	for (fc_i = 0; fc_i < fc_query->size; fc_i++)
	{
		pq_sendint8(&fc_buf, fc_item->type);

		switch (fc_item->type)
		{
			case QI_VAL:
				pq_sendint8(&fc_buf, fc_item->qoperand.weight);
				pq_sendint8(&fc_buf, fc_item->qoperand.prefix);
				pq_sendstring(&fc_buf, GETOPERAND(fc_query) + fc_item->qoperand.distance);
				break;
			case QI_OPR:
				pq_sendint8(&fc_buf, fc_item->qoperator.oper);
				if (fc_item->qoperator.oper == OP_PHRASE)
					pq_sendint16(&fc_buf, fc_item->qoperator.distance);
				break;
			default:
				elog(ERROR, "unrecognized tsquery node type: %d", fc_item->type);
		}
		fc_item++;
	}

	PG_FREE_IF_COPY(fc_query, 0);

	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

Datum tsqueryrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	TSQuery		fc_query;
	int			fc_i,
				fc_len;
	QueryItem  *fc_item;
	int			fc_datalen;
	char	   *fc_ptr;
	uint32		fc_size;
	const char **fc_operands;
	bool		fc_needcleanup;

	fc_size = pq_getmsgint(fc_buf, sizeof(uint32));
	if (fc_size > (MaxAllocSize / sizeof(QueryItem)))
		elog(ERROR, "invalid size of tsquery");

	/* 分配空间以临时保存操作数字符串 */
	fc_operands = palloc(fc_size * sizeof(char *));

	/* 为所有 QueryItems 分配空间。 */
	fc_len = HDRSIZETQ + sizeof(QueryItem) * fc_size;
	fc_query = (TSQuery) palloc0(fc_len);
	fc_query->size = fc_size;
	fc_item = GETQUERY(fc_query);

	fc_datalen = 0;
	for (fc_i = 0; fc_i < fc_size; fc_i++)
	{
		fc_item->type = (int8) pq_getmsgint(fc_buf, sizeof(int8));

		if (fc_item->type == QI_VAL)
		{
			size_t		fc_val_len;	/* 转码到服务器后长度 */
			uint8		fc_weight;
			uint8		fc_prefix;
			const char *fc_val;
			pg_crc32	fc_valcrc;

			fc_weight = (uint8) pq_getmsgint(fc_buf, sizeof(uint8));
			fc_prefix = (uint8) pq_getmsgint(fc_buf, sizeof(uint8));
			fc_val = pq_getmsgstring(fc_buf);
			fc_val_len = strlen(fc_val);

			/* 合理性检查 */

			if (fc_weight > 0xF)
				elog(ERROR, "invalid tsquery: invalid weight bitmap");

			if (fc_val_len > MAXSTRLEN)
				elog(ERROR, "invalid tsquery: operand too long");

			if (fc_datalen > MAXSTRPOS)
				elog(ERROR, "invalid tsquery: total operand length exceeded");

			/* 看起来有效。 */

			INIT_LEGACY_CRC32(fc_valcrc);
			COMP_LEGACY_CRC32(fc_valcrc, fc_val, fc_val_len);
			FIN_LEGACY_CRC32(fc_valcrc);

			fc_item->qoperand.weight = fc_weight;
			fc_item->qoperand.prefix = (fc_prefix) ? true : false;
			fc_item->qoperand.valcrc = (int32) fc_valcrc;
			fc_item->qoperand.length = fc_val_len;
			fc_item->qoperand.distance = fc_datalen;

			/*
			 * 操作数字符串在这个循环后复制到最终结构；
			 * 在这里我们只是将它们收集到数组中
			 */
			fc_operands[fc_i] = fc_val;

			fc_datalen += fc_val_len + 1; /* + 1 以容纳 '\0' 终止符 */
		}
		else if (fc_item->type == QI_OPR)
		{
			int8		fc_oper;

			fc_oper = (int8) pq_getmsgint(fc_buf, sizeof(int8));
			if (fc_oper != OP_NOT && fc_oper != OP_OR && fc_oper != OP_AND && fc_oper != OP_PHRASE)
				elog(ERROR, "invalid tsquery: unrecognized operator type %d",
					 (int) fc_oper);
			if (fc_i == fc_size - 1)
				elog(ERROR, "invalid pointer to right operand");

			fc_item->qoperator.oper = fc_oper;
			if (fc_oper == OP_PHRASE)
				fc_item->qoperator.distance = (int16) pq_getmsgint(fc_buf, sizeof(int16));
		}
		else
			elog(ERROR, "unrecognized tsquery node type: %d", fc_item->type);

		fc_item++;
	}

	/* 扩大缓冲区以为操作数值腾出空间。 */
	fc_query = (TSQuery) repalloc(fc_query, fc_len + fc_datalen);
	fc_item = GETQUERY(fc_query);
	fc_ptr = GETOPERAND(fc_query);

	/*
	 * 填写左侧指针。检查树的结构是否良好作为副作用。
	 */
	fc_findoprnd(fc_item, fc_size, &fc_needcleanup);

	/* 不能找到任何 QI_VALSTOP 节点 */
	Assert(!fc_needcleanup);

	/* 将操作数复制到输出结构 */
	for (fc_i = 0; fc_i < fc_size; fc_i++)
	{
		if (fc_item->type == QI_VAL)
		{
			memcpy(fc_ptr, fc_operands[fc_i], fc_item->qoperand.length + 1);
			fc_ptr += fc_item->qoperand.length + 1;
		}
		fc_item++;
	}

	pfree(fc_operands);

	Assert(fc_ptr - GETOPERAND(fc_query) == fc_datalen);

	SET_VARSIZE(fc_query, fc_len + fc_datalen);

	PG_RETURN_TSQUERY(fc_query);
}

/*
 * 调试函数，仅用于查看查询
 * 将在索引中的非叶页面中执行
 */
Datum tsquerytree(PG_FUNCTION_ARGS)
{
	TSQuery		fc_query = PG_GETARG_TSQUERY(0);
	INFIX		fc_nrm;
	text	   *fc_res;
	QueryItem  *fc_q;
	int			fc_len;

	if (fc_query->size == 0)
	{
		fc_res = (text *) palloc(VARHDRSZ);
		SET_VARSIZE(fc_res, VARHDRSZ);
		PG_RETURN_POINTER(fc_res);
	}

	fc_q = clean_NOT(GETQUERY(fc_query), &fc_len);

	if (!fc_q)
	{
		fc_res = cstring_to_text("T");
	}
	else
	{
		fc_nrm.curpol = fc_q;
		fc_nrm.buflen = 32;
		fc_nrm.cur = fc_nrm.buf = (char *) palloc(sizeof(char) * fc_nrm.buflen);
		*(fc_nrm.cur) = '\0';
		fc_nrm.op = GETOPERAND(fc_query);
		fc_infix(&fc_nrm, -1, false);
		fc_res = cstring_to_text_with_len(fc_nrm.buf, fc_nrm.cur - fc_nrm.buf);
		pfree(fc_q);
	}

	PG_FREE_IF_COPY(fc_query, 0);

	PG_RETURN_TEXT_P(fc_res);
}
